package hk.linkcredit.walletservice.wallet.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import hk.linkcredit.walletservice.constant.Status;
import hk.linkcredit.walletservice.constant.WalletConstants;
import hk.linkcredit.walletservice.exception.BizException;
import hk.linkcredit.walletservice.utils.DictUtils;
import hk.linkcredit.walletservice.utils.GzipUtils;
import hk.linkcredit.walletservice.utils.IdUtils;
import hk.linkcredit.walletservice.utils.StringUtils;
import hk.linkcredit.walletservice.utils.core.RedisCache;
import hk.linkcredit.walletservice.wallet.domain.entity.WalletAccount;
import hk.linkcredit.walletservice.wallet.domain.entity.WalletAmount;
import hk.linkcredit.walletservice.wallet.domain.vo.WalletAccountInfo;
import hk.linkcredit.walletservice.wallet.domain.vo.WalletAccountReq;
import hk.linkcredit.walletservice.wallet.domain.vo.WalletAccountVo;
import hk.linkcredit.walletservice.wallet.domain.vo.WalletStatusReq;
import hk.linkcredit.walletservice.wallet.mapper.WalletAccountMapper;
import hk.linkcredit.walletservice.wallet.service.*;
import hk.linkcredit.walletservice.wallet.service.strategy.BalanceService;
import hk.linkcredit.walletservice.wallet.service.strategy.BalanceStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 钱包账户表 服务实现类
 * </p>
 *
 * @author LinkCredit
 * @since 2022-06-10 01:23:44
 */
@Slf4j
@Service
public class WalletAccountServiceImpl extends ServiceImpl<WalletAccountMapper, WalletAccount> implements WalletAccountService {

    @Autowired
    WalletAccountMapper walletAccountMapper;

    @Autowired
    WalletAmountService walletAmountService;

    @Autowired
    WalletFlowService walletFlowService;

    @Autowired
    BalanceService balanceService;

    @Autowired
    SysDictDataService dictDataService;

    @Autowired
    AsyncService asyncService;

    @Autowired
    RedisCache redisCache;

    @Override
    public WalletAccountVo getWalletAccount(String custNo) {

        WalletAccountVo walletAccount = walletAccountMapper.getWalletAccount(custNo);
        if (walletAccount == null) {
            throw new BizException(Status.WALLE_ACCOUN_NOT_EXIST);
        }

        walletAccount.setMinWithdrawBalance(getWalletConfigValue("min_withdraw_balance"));
        walletAccount.setMinWithdrawBonus(getWalletConfigValue("min_withdraw_bonus"));
        walletAccount.setServiceRate(getWalletConfigValue("service_rate"));

        return walletAccount;
    }

    @Override
    public WalletAccountInfo getWalletAccountInfo(String custNo) {
        log.info("查询钱包账户信息 >>> custNo = {}", custNo);

        WalletAccountInfo walletAccount = walletAccountMapper.getWalletAccountInfo(custNo);
        if (walletAccount == null) {
            throw new BizException(Status.WALLE_ACCOUN_NOT_EXIST);
        }

        walletAccount.setMinWithdrawBalance(getWalletConfigValue("min_withdraw_balance"));
        walletAccount.setMinWithdrawBonus(getWalletConfigValue("min_withdraw_bonus"));
        walletAccount.setServiceRate(getWalletConfigValue("service_rate"));

        return walletAccount;
    }

    @Override
    public void checkWalletBalance(String walletType, BigDecimal amount, WalletAccountInfo walletAccountInfo) {
        BalanceStrategy balanceStrategy = balanceService.getBalanceStrategy(getWalletBeanType(walletType));
        balanceStrategy.checkAmount(amount, walletAccountInfo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateWalletAccount(WalletAccountReq walletAccountReq) {
        log.info("更新钱包账户信息（Income） >>> walletAccountReq = {}", walletAccountReq);

        WalletAccountInfo walletAccountInfo = this.getWalletAccountInfo(walletAccountReq.getCustNo());

        String walletType = getWalletType(walletAccountReq.getFundsType());
        walletFlowService.createWalletFlowIncome(walletType, walletAccountReq, walletAccountInfo);

        walletAmountService.updateWalletAccountAmount(walletType, walletAccountReq.getAmount(), walletAccountInfo);

        log.info("完成更新钱包账户信息（Income） >>> ");
    }

    @Override
    public JSONObject splitAmount(String walletStatus, BigDecimal actualAmt, BigDecimal availableBalance, BigDecimal availableBonus) {
        log.info("拆分金额 >>> actualAmt = {}，availableBalance = {}，availableBonus = {}", actualAmt, availableBalance, availableBonus);

        BigDecimal balanceAmt = BigDecimal.ZERO;
        BigDecimal bonusAmt = BigDecimal.ZERO;
        BigDecimal dealAmt = actualAmt;

        JSONObject splitAmtJson = new JSONObject();
        splitAmtJson.put("actualAmt", actualAmt);
        splitAmtJson.put("balanceAmt", balanceAmt);
        splitAmtJson.put("bonusAmt", bonusAmt);

        if (WalletConstants.WALLET_STATUS_LOCK.equals(walletStatus)) {
            return splitAmtJson;
        }

        if (!WalletConstants.WALLET_STATUS_BONLUS_LOCK.equals(walletStatus)) {
            if (dealAmt.subtract(availableBonus).compareTo(BigDecimal.ZERO) > 0) {
                dealAmt = dealAmt.subtract(availableBonus);
                bonusAmt = availableBonus;
            } else {
                bonusAmt = dealAmt;
                dealAmt = BigDecimal.ZERO;
            }
        }

        if (!WalletConstants.WALLET_STATUS_BALANCE_LOCK.equals(walletStatus)) {
            if (dealAmt.subtract(availableBalance).compareTo(BigDecimal.ZERO) > 0) {
                dealAmt = dealAmt.subtract(availableBalance);
                balanceAmt = availableBalance;
            } else {
                balanceAmt = dealAmt;
                dealAmt = BigDecimal.ZERO;
            }
        }

        splitAmtJson.put("balanceAmt", balanceAmt);
        splitAmtJson.put("bonusAmt", bonusAmt);

        log.info("拆分金额结果 >>> splitAmtJson = {}", splitAmtJson);
        return splitAmtJson;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initWalletAccountAll(String custNo) {
        log.info("初始化钱包账户及金融账户 >>> custNo = {}", custNo);

        String walletNo = IdUtils.accountNo();
        initWalletAccoun(custNo, walletNo);
        initWalletAmount(custNo, walletNo);

        log.info("完成钱包账户及金融账户初始化 >>> ");
    }

    @Override
    public void updateWalletStatus(WalletStatusReq walletStatusReq) {
        log.info("更新钱包状态 >>> custNo = {}，walletStatus = {}", walletStatusReq.getCustNo(), walletStatusReq.getWalletStatus());

        UpdateWrapper<WalletAccount> wrapper = Wrappers.update();
        wrapper.lambda()
                .set(WalletAccount::getWalletStatus, walletStatusReq.getWalletStatus())
                .eq(WalletAccount::getCustNo, walletStatusReq.getCustNo());
        this.update(null, wrapper);

        log.info("结束更新钱包状态 >>> ");
    }

    @Override
    public JSONObject getCustomersWithBalance() {
        log.info("开始获取存在余额的客户 >>>");

        List<String> custNoList = walletAmountService.getCustomersWithBalance();

        StringBuilder custNos = new StringBuilder();
        custNoList.stream().forEach(p -> custNos.append(p).append(","));

        JSONObject result = new JSONObject();
        result.put("custNos", GzipUtils.compress(custNos.deleteCharAt(custNos.length() - 1).toString()));

        log.info("完成获取存在余额的客户 >>>");
        return result;
    }

    private void initWalletAmount(String custNo, String walletNo) {
        log.info("初始化钱包金融账户 >>> custNo = {}，walletNo = {}", custNo, walletNo);

        WalletAmount walletAmountBalance = WalletAmount.builder()
                .walletNo(walletNo)
                .custNo(custNo)
                .walletType(WalletConstants.WALLET_TYPE_BALANCE_1)
                .build();

        WalletAmount walletAmountBonus = WalletAmount.builder()
                .walletNo(walletNo)
                .custNo(custNo)
                .walletType(WalletConstants.WALLET_TYPE_BONUS_2)
                .build();

        List<WalletAmount> walletAmountList = new ArrayList<>();
        walletAmountList.add(walletAmountBalance);
        walletAmountList.add(walletAmountBonus);
        walletAmountService.saveBatch(walletAmountList);

        log.info("完成钱包金融账户初始化 >>> ");
    }

    private void initWalletAccoun(String custNo, String walletNo) {
        log.info("初始化钱包账户 >>> custNo = {}，walletNo = {}", custNo, walletNo);

        WalletAccount walletAccount = WalletAccount.builder()
                .walletNo(walletNo)
                .custNo(custNo)
                .walletStatus(WalletConstants.WALLET_STATUS_NORMAL)
                .build();
        this.save(walletAccount);

        log.info("完成钱包账户初始化 >>> ");
    }

    private BigDecimal checkTargetAmt(BigDecimal targetAmt, BigDecimal currentAmt) {
        BigDecimal actualAmount = BigDecimal.ZERO;
        if (currentAmt.compareTo(BigDecimal.ZERO) <= 0) {
            actualAmount = BigDecimal.ZERO;
        }

        if (targetAmt.compareTo(currentAmt) <= 0) {
            actualAmount = targetAmt;
        }

        if (targetAmt.compareTo(currentAmt) > 0) {
            actualAmount = currentAmt;
        }

        return actualAmount;
    }

    private String getWalletBeanType(String walletType) {
        if (StringUtils.equals(WalletConstants.WALLET_ORDER_TYPE_BALANCE, walletType)) {
            return WalletConstants.WALLET_TYPE_BALANCE;
        }

        if (StringUtils.equals(WalletConstants.WALLET_ORDER_TYPE_BONUS, walletType)) {
            return WalletConstants.WALLET_TYPE_BONUS;
        }

        throw new BizException(Status.WALLE_WITHDRAW_TYPE_CANNOT_SUPPORT);
    }

    @Override
    public String getWalletType(String fundsType) {
        switch (fundsType) {
            case WalletConstants.WALLET_FLOW_FUNDS_TYPE_INVITE_FRIENDS:
            case WalletConstants.WALLET_FLOW_FUNDS_TYPE_VIP_REWARD:
                return WalletConstants.WALLET_ORDER_TYPE_BONUS;
            case WalletConstants.WALLET_FLOW_FUNDS_TYPE_OVERFLOW:
            case WalletConstants.WALLET_FLOW_FUNDS_TYPE_BIND_CARD:
            case WalletConstants.WALLET_FLOW_FUNDS_TYPE_MONNIFY_CHARGE:
            case WalletConstants.WALLET_FLOW_FUNDS_TYPE_PAYSTACK_CHARGE:
                return WalletConstants.WALLET_ORDER_TYPE_BALANCE;
            default:
                throw new BizException(Status.WALLE_TYPE_CANNOT_SUPPORT);
        }
    }

    private BigDecimal getWalletConfigValue(String configName) {
        return new BigDecimal(DictUtils.getDictLabel("wallet_withdraw_config", configName));
    }
}
